(*
 * PdP ML
 * 
 * Francesco Cotto 
 *
 * Es.5a Liste lazy in ML: Eratostene 
 *
 *)

type 'a llist = Lnull | Lcons of ( unit -> ('a * 'a llist) ) ;;

exception NullException ;;

(* b^e *)
let rec exp b e = match e with 
	0 -> 1
	| e -> b * exp b (e-1);;



(* genera una ll-serie nella forma x_i = f(x_(i-1)) *)
let rec myFrom cv fNextValue = 
        let nv = fNextValue cv in
        Lcons (function () -> (cv, (myFrom nv fNextValue)));;

(* genera una lazy list infinita a partire dal numero n*)
let rec from n = myFrom n ((+)1) ;;


let rec llmap f ll = 
        match ll with
        Lnull -> raise NullException
        | Lcons y -> let newValue = f (fst (y())) in
                     Lcons (function () -> (newValue, llmap f (snd(y()))));;

(* restituisce una lista con gli elementi della lazy list fino a quando la cond
 * e' vera 
 * val takeWhile : ('a -> bool) -> 'a llist -> 'a list = <fun>
 *)
let rec takeWhile cond ll = 
        match ll with
        Lnull -> raise NullException
        | Lcons y -> let v = fst(y()) in
                     if (cond v) 
                        then v::(takeWhile cond (snd(y())))
                        else [] ;;


(* semplice alias/macro su come usare la takeWhile 
 * lmf = list/limited map-function *)
let lmf cond fmap ll = 
        takeWhile cond (llmap fmap ll);;

(* funzione principale *)
let potenze base limite = 
        takeWhile ((>=) limite )       (llmap (exp base) (from 0));;
(*      tW        [cond.term.]->bool   [map    funz. mapping   ll] -> ll    *)


(* come sopra -potenza- ma usando lmf *)
let limitedPotenza base limite = 
        lmf ((>=) limite) (exp base) (from 0);;


(* Esempio di utilizzo:
 *
 * limitedPotenza 2 20000;;
 *
 *)

(* altri esempi *)
let rec fact = 
        function 
                0 -> 1
                | x -> x * (fact (x-1));;

let pari = function x -> (x mod 2) = 0 ;;

(* linear congruential generator: pseudorandom generator *)
let myLCG x =
        let a = 214013 in
        let c = 2531011 in  
        let m = exp 2 16 in
        (((a*x) mod m) + c) mod m  ;;

let limitedFact limite = 
        takeWhile ((>=) limite) (llmap fact (from 0));;

let limitedMyLCG finoA=
        takeWhile ((!=) finoA) (myFrom 101 myLCG) ;;


(* stessi esericizi ma con lazylist nella forma (mapVal, val) *)
let rec llzip lla llb = 
        match lla, llb with
        Lcons a, Lcons b -> Lcons (function () -> (fst (a()), fst (b())), llzip (snd (a ())) (snd (b()))) 
        | _ -> raise NullException ;; 

let takeWhileZip cond ll = 
        let zipCond = function x -> cond (fst x) in
        takeWhile (zipCond) ll;;

let rec llmapZip f ll = 
        match ll with
        Lnull -> raise NullException
        | Lcons y -> let newValue = f (fst (y())) in
                     Lcons (function () -> ((newValue, fst (y())), llmapZip f (snd(y()))));;

(* lmf = limited map-function *)
let lmfZip cond f ll =
        takeWhileZip cond (llmapZip f (ll));;


(* esempi con ll-zip *)
let limitedFactZip limite = 
        lmfZip ((>=) limite) (fact) (from 0);;

let limitedPotenzaZip base limite =
        lmfZip ((>=) limite) (exp base) (from 0) ;;


(* esempi di utilizzo / main *)
print_string "Potenze base 2 fino a 2000" ;;
limitedPotenza 2 2000;;
limitedPotenzaZip 2 2000;;

print_string "Fattoriale fino a 100000" ;;
limitedFact 100000;;
limitedFactZip 100000;;

print_string "improbabile generatore numeri pseudocasauali fino a 303";;
limitedMyLCG 303;;
